home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / EDIT_UTL / MED295 / MED295.PAS < prev   
Pascal/Delphi Source File  |  1995-02-13  |  35KB  |  1,058 lines

  1. program med295;  
  2.   med295.pas: full screen editor, Version: 295 (of Feb-95).
  3.   (GNU-CopyLeft) Mohsin Ahmed, mosh@cs.albany.edu
  4.   Free for non-commercial use, can be used as an editor toolbox!
  5.   Compiles with: Turbo Pascal 5.0, used and tested on IBM/XT & AT.
  6.   Speed: Good enough on a 286 AT with all range checks on.
  7.   ----------------------------------------------------------------------
  8.   Usage:
  9.     C:\> editor FileName
  10.   Max line length is LineSz (80) chars, it will split longer lines.
  11.   Edit keys:
  12.              BackSpace    :  delete char before cursor.
  13.              Delete       :  delete char under cursor.
  14.              Insert       :  toggle insert/overwrite mode.
  15.              Return       :  Break line in Insert mode, else next line,
  16.              Tab          :  Insert 10 spaces,
  17.              F1,F2        :  +/- Foreground Color.
  18.              F3,F4        :  +/- Background Color.
  19.   << Arrow keys >>
  20.              Left/Right   : Char Left/Right,
  21.              C-Left/Right : Word Left/Right,
  22.              C-Home/End   : Delete to line begin/end,
  23.              Up/Dn        : Prev/Next line,
  24.              PgUp/PgDn    : Prev/Next Page,
  25.              C-PgUp/PgDn  : First/Last Line,
  26.   << Control (C-) keys >>
  27.              C-J          : Join with next line.
  28.              C-T          : Delete word.
  29.              C-Y          : Delete line.
  30.   << Esc (Meta-) keys >>
  31.              M-d          : Delete this line,
  32.              M-D          : Delete lines upto the marked line,
  33.              M-f          : Change current File Name,
  34.              M-j          : Join this and next line,
  35.              M-m          : Mark this line,
  36.              M-p/P        : Put yanked/Yanked line, at eol,
  37.              M-q          : Quit.
  38.              M-r          : Read from another file,
  39.              M-u          : Undo current line, restore deleted line,
  40.              M-x          : Write file and quit,
  41.              M-y/Y        : Yank line, to eol,
  42.              M-w          : Write to another file,
  43.              M-/string    : search for string (case sensitive),
  44.  
  45.   << SCREEN-LAYOUT >>
  46.   01 Date and Time.                       RowDate
  47.   02 Col,Row,Lines,Mode Info.             RowMode
  48.   03--------------------------            RowTop
  49.   04                                      RowMin
  50.   ..
  51.   19 CurPtr^.prev^.dat
  52.   20 CurPtr^.dat                          Row
  53.   21 CurPtr^.next^.dat
  54.   22                                      RowMax
  55.   23--------------------------            RowBot
  56.   24 Reading/Writing/:ESC-Cmds/FileName   RowMeta
  57.   25 Line Number                          RowNum
  58.  
  59.   If you have a job, I have the time and skills, send email to
  60.     mosh@cs.albany.edu, also try finger mosh@cse.iitb.ernet.in
  61.     Tel. 518-432-9662 (H) (USA).
  62.  
  63. --------------------------------------------------------------------------}
  64. uses dos,crt;
  65. Const MaxLines   = 32000;
  66.       LineSz     = 80;       RowDate = 01;     RowMode= 02;
  67.       ESC        = #27;      RowMeta = 24;     RowNum = 25;
  68.       ExtKeyCode = #0;       RowTop  = 03;     RowBot = 23;
  69.       Space      = ' ';      RowMin  = 04;     RowMax = 22;
  70. type  strA = string[20];
  71.       strB = string[LineSz];
  72.       strC = string[255];
  73.      lineptr = ^linerec;
  74.      linerec = record
  75.                  prev, next : lineptr ;
  76.                  data       : StrB  ;
  77.                end;
  78. var TextFile       { file handle for i/o } : text ;
  79.     FileBuffer     { file buffer         } : array[0..20000] of char;
  80.     FileName,      { file for read/write }
  81.     FileNameTmp    { file for tmp read/write } : strA;
  82.     TopPtr,        { ptr to first   line }
  83.     BotPtr,        { ptr to last    line }
  84.     CurPtr,        { ptr to current line }
  85.     LineMark,      { ptr to marked  line }
  86.     tp1, tp2, tp3  { temp ptr            } : lineptr ;
  87.     Row            { current row         } : RowMin..RowMax;
  88.     Col            { current column      } : 0..LineSz+1 ;
  89.     i              { temp var            } : 0..LineSz+1 ;
  90.     LineCount,     { total line count    }
  91.     LastCount,     { previous total line count }
  92.     LastLineNum,   { previous row        }
  93.     LineNumber     { current line number } : 0..MaxLines;
  94.     UnDoCopy,      { copy for 'undo'     }
  95.     TmpCopy,       { For Swapping String }
  96.     YankCopy       { copy for 'yank'     } : StrB ;
  97.     ikey, extkey,  { input key           }
  98.     cmdkey         { esc command key     } : char ;
  99.     InsMode,       { Insert/Overwrite    }
  100.     ReDisplay      { for screen update   } : boolean;
  101.  
  102.   { ----------------------------------------------------------------------- }
  103.  
  104.   procedure RingBell;
  105.     begin
  106.     sound( 800 );
  107.     delay( 50 );
  108.     nosound;
  109.     end;
  110.   {----------------------------------------------------------}
  111.  
  112.   procedure SoundClick;
  113.     begin
  114.     sound( 100 );
  115.     delay(10);
  116.     nosound;
  117.     end;
  118.   { ----------------------------------------------------------------------- }
  119.  
  120.   { reads in a name of a file, returns false if the input is empty }
  121.   function getname( var FileNameTmp : strA ):boolean;
  122.       begin
  123.       write(' FileName(Tmp): ');
  124.       readln(FileNameTmp);
  125.       getname := ( length( FileNameTmp ) > 0 )
  126.       end;
  127.   { ----------------------------------------------------------------------- }
  128.  
  129.   { Removes tail spaces from a line, if line ended with a spaces.
  130.     It leaves one space at end of the line.
  131.     It doesn't modify the first char of the line.
  132.   }
  133.   procedure cleartailspaces( p : LinePtr );
  134.       var i : integer;
  135.      begin
  136.      i := length( p^.data );
  137.      while (i > 1) and (p^.data[i] = Space) do { find last non-space }
  138.        i := i-1;
  139.      if i < length( p^.data ) then { leave one space at end }
  140.         begin
  141.         { i:=i+1; added below }
  142.         delete( p^.data, i+2, 255 );
  143.         end;
  144.      end;
  145.   { ----------------------------------------------------------------------- }
  146.  
  147.   { Removes all leading spaces from a line.
  148.     It doesn't modify the first char of the line.
  149.   }
  150.   procedure clearleadingspaces( p : LinePtr );
  151.       var i : integer;
  152.      begin
  153.      i := 1;
  154.      while(i < length( p^.data)) and (p^.data[i] = Space) do
  155.        { find first non-space }
  156.        i := i+1;
  157.      delete( p^.data, 1, i-1 );
  158.      end;
  159.  
  160.   { ----------------------------------------------------------------------- }
  161.   { Add BigStr at tp1 and repoint tp1 for next call,
  162.     If BigStr is too big, then
  163.     Split BigStr between col [MinCut,..,LineSize-1] at a space,
  164.     If no space is found then split at col[LineSz-1].
  165.   }
  166.   Const MinCut = 40;
  167.   Procedure addline( var tp1 : LinePtr; BigStr : StrC );
  168.        var cutat : integer;
  169.     begin
  170.     while length( BigStr ) > 0 do
  171.       begin
  172.       new( tp2 );
  173.       if length( BigStr ) > LineSz then       { breakline }
  174.          begin
  175.          cutat := LineSz-1;
  176.          while (cutat > MinCut) and (BigStr[cutat+1] <> ' ') do
  177.             cutat := cutat-1;             { search for a suitable break }
  178.          if BigStr[cutat+1] <> ' ' then            { no break was found }
  179.             tp2^.data := copy( BigStr, 1, LineSz-1 ) + '\'
  180.          else
  181.             tp2^.data := copy( BigStr, 1, cutat );
  182.          delete( BigStr, 1, cutat );
  183.          end
  184.       else
  185.          begin
  186.          tp2^.data := BigStr ;
  187.          BigStr := '';
  188.          end;
  189.       if tp1^.next = nil then
  190.          begin            { currently at last line, add a line }
  191.          tp1^.next := tp2 ;
  192.          tp2^.next := nil ;
  193.          tp2^.prev := tp1 ;
  194.          BotPtr := tp2 ;
  195.          end
  196.       else
  197.          begin            { currently at middle line, insert a line }
  198.          tp3 := tp1^.next ;
  199.          tp2^.prev := tp1;
  200.          tp2^.next := tp3 ;
  201.          tp1^.next := tp2 ;
  202.          tp3^.prev := tp2 ;
  203.          end;             { note: we cannot add before line 1 }
  204.       tp1 := tp2 ;
  205.       LineCount := LineCount +1;
  206.       gotoxy(3,RowNum); write(' Line ', LineCount ) ;
  207.       if length( BigStr ) > 0 then
  208.          begin
  209.          soundclick;
  210.          write('Line Split at ',cutat:2);
  211.          end;
  212.       clreol;
  213.       end; { while }
  214.     end;
  215.   {----------------------------------------------------------------------- }
  216.  
  217.   procedure readfile( FileName: StrA );
  218.        var bigstr : strC;
  219.      begin{readfile}
  220.      assign( TextFile ,FileName );
  221.      settextbuf( TextFile, FileBuffer );
  222.      {$I-} reset( TextFile ); {$I+}
  223.      if IOResult = 0 then
  224.         begin
  225.         gotoxy(3,RowMeta); clreol; write('Reading: ',FileName);
  226.         tp1 := CurPtr ;
  227.         while not eof( TextFile ) do
  228.            begin
  229.            readln( TextFile, bigstr ) ;
  230.            addline( tp1, bigstr );
  231.            end; { while not eof }
  232.         close( TextFile );
  233.         ReDisplay := true;
  234.         end { IoResult = 0 }
  235.      else
  236.         begin
  237.         gotoxy(3,RowMeta); clreol; write('Cannot Read: ',FileName);
  238.         RingBell;
  239.         delay(500);
  240.         end;
  241.      end{readfile};
  242.   { ----------------------------------------------------------------------- }
  243.  
  244.   procedure writefile( FileName: StrA );
  245.        var TmpData : strB ;
  246.            ThisLine: integer;
  247.      begin{writefile}
  248.      gotoxy(3,RowMeta); clreol; write('Writing : ',FileName);
  249.      assign( TextFile, FileName );
  250.      settextbuf( TextFile, FileBuffer );
  251.      {$I-} rewrite( TextFile ); {$I+}
  252.      if IOResult <> 0 then
  253.         begin
  254.         gotoxy(1,RowNum); write('Cannot write file: ',FileName );
  255.         RingBell; delay(2000);
  256.         exit;
  257.         end;
  258.      ThisLine := 0;      { start from the beginning }
  259.      tp1 := TopPtr ;
  260.      while tp1 <> nil do
  261.         begin
  262.         ThisLine := ThisLine + 1;
  263.         gotoxy(3,RowNum); write(' Line ', ThisLine:4,' / ', LineCount:4 ) ;
  264.         writeln( TextFile, tp1^.data );
  265.         tp1 := tp1^.next ;
  266.         end; { end while }
  267.      close( TextFile );
  268.      end{writefile};
  269.   { ----------------------------------------------------------------------- }
  270.  
  271.   var Laststr : StrB;                 { Stores last search string }
  272.  
  273.   procedure searchstring;
  274.     var
  275.        SearchStr      { Search String       } : StrB;
  276.        FoundPos,      { Pos of SearchStr    }
  277.        FoundLine      { Pos of SearchStr    } : integer;
  278.  
  279.      begin{searchstring}
  280.      readln( SearchStr );
  281.      if length( SearchStr ) = 0 then  SearchStr := LastStr
  282.      else                             LastStr := SearchStr;
  283.      gotoxy(1,RowNum); write('Searching: <',SearchStr,'>');
  284.      tp1 := CurPtr ;
  285.      FoundLine := LineNumber ;
  286.      FoundPos := 0;
  287.      { search from next line to bottom line }
  288.      while ( FoundPos = 0 ) and ( tp1^.next <> nil ) do
  289.          begin
  290.          tp1 := tp1^.next ;
  291.          FoundLine := FoundLine + 1;
  292.          FoundPos := pos( SearchStr, tp1^.data );
  293.          end; { of search till bottom }
  294.      { Wrap around, and search from top }
  295.      if FoundPos = 0 then
  296.         begin
  297.         tp1 := TopPtr ;
  298.         FoundLine := 1;
  299.         FoundPos :=  pos( SearchStr, tp1^.data );
  300.         while ( FoundPos = 0 ) and ( tp1 <> CurPtr ) do
  301.             begin
  302.             tp1 := tp1^.next ;
  303.             FoundLine := FoundLine + 1;
  304.             FoundPos := pos( SearchStr, tp1^.data );
  305.             end;
  306.         end; { of search from top }
  307.         if FoundPos > 0 then
  308.            begin
  309.            col := FoundPos ;
  310.            LineNumber := FoundLine ;
  311.            CurPtr := tp1 ;
  312.            ReDisplay := true;
  313.            write('- Found on line: ',FoundLine:4);
  314.            end
  315.         else
  316.            begin
  317.            write('- Not Found');
  318.            RingBell;
  319.            end;
  320.      delay( 1000 );
  321.      end{searchstring};
  322.   { ----------------------------------------------------------------------- }
  323.  
  324.   procedure delete_line( var ThisPtr : LinePtr );
  325.      begin{delete_line}
  326.      UnDoCopy := ThisPtr^.data ;
  327.      YankCopy := UnDoCopy ;
  328.      if (ThisPtr^.prev = nil) and (ThisPtr^.next = nil) then
  329.         { LineCount = 1 }
  330.         begin
  331.         ThisPtr^.data:=Space;
  332.         exit;                  { go back now }
  333.         end
  334.      else if (ThisPtr^.prev = nil) and (ThisPtr^.next <> nil) then
  335.         { first line, LineNumber = 1 }
  336.         begin
  337.         ThisPtr := ThisPtr^.next ;
  338.         ThisPtr^.prev := nil ;
  339.         dispose( TopPtr );
  340.         TopPtr := ThisPtr ;
  341.         LineNumber := 1;
  342.         LineCount := LineCount - 1;
  343.         end
  344.      else if (ThisPtr^.prev <> nil) and  (ThisPtr^.next = nil) then
  345.         { last line, LineNumber = LineCount }
  346.         begin
  347.         ThisPtr := ThisPtr^.prev ;
  348.         ThisPtr^.next := nil ;
  349.         dispose( BotPtr );
  350.         BotPtr := ThisPtr ;
  351.         LineCount := LineCount - 1;
  352.         { LineNumber := LineNumber - 1; }
  353.         LineNumber := LineCount;
  354.         end
  355.      else if (ThisPtr^.prev <> nil) and (ThisPtr^.next <> nil ) then
  356.         { middle line, LineNumber in [ 2 .. LineCount-1 ] }
  357.         begin
  358.         tp1 := ThisPtr^.prev ;
  359.         tp3 := ThisPtr^.next ;
  360.         tp1^.next := tp3 ;
  361.         tp3^.prev := tp1 ;
  362.         dispose( ThisPtr );
  363.         ThisPtr := tp3 ;
  364.         LineCount := LineCount - 1;
  365.         end;
  366.      ReDisplay := true;
  367.      gotoxy(1,RowNum); write('Line ',LineCount,'-',LineNumber,' deleted.');
  368.      { soundclick; }
  369.      gotoxy(1,RowNum); clreol;
  370.      end{delete_line};
  371.   { ----------------------------------------------------------------------- }
  372.  
  373.   { Break this line into two lines,
  374.     not wrapmode  -> break at current column,
  375.         wrapmode  -> break at word boundary (ie. a space ).
  376.   }
  377.   procedure breakline( wrapmode: boolean );
  378.      var i : integer;
  379.     begin{breakline}
  380.     new( tp2 );
  381.     tp1 := CurPtr ;
  382.     tp2^.next := tp1^.next ;
  383.     tp2^.prev := tp1 ;
  384.     tp1^.next := tp2 ;
  385.     tp3 := tp2^.next ;
  386.     tp3^.prev := tp2 ;
  387.     CurPtr := tp2 ;
  388.  
  389.     if wrapmode then
  390.       begin
  391.       i := col; { cursor is always ahead of current char, so always -1 }
  392.       while (1 < i) and (tp1^.data[i-1] <> Space) do { find place to break }
  393.          i:= i-1;
  394.       if i < col then { if a word was pulled to next line }
  395.          soundclick;
  396.       if i = 1 then   { the word was too long - upto col = 1, ignore }
  397.          i := col;
  398.       end
  399.     else
  400.       i := col;
  401.  
  402.     { divide data into two parts at i }
  403.     tp2^.data := copy( tp1^.data, i, length( tp1^.data ) - i +1 );
  404.     delete( tp1^.data, i, length( tp1^.data ) - i + 1 );
  405.  
  406.     if wrapmode then  col := length( tp2^.data )
  407.     else              col := 1;
  408.  
  409.     if CurPtr^.next = nil  then
  410.        BotPtr := CurPtr;
  411.     LineCount := LineCount + 1 ;
  412.     LineNumber := LineNumber + 1 ;
  413.     UnDoCopy := CurPtr^.data ;
  414.     ReDisplay := true;
  415.     end{breakline};
  416.   {----------------------------------------------------------}
  417.   procedure goto_previous_line;
  418.      begin
  419.      if CurPtr^.prev <> nil then
  420.           begin
  421.           CurPtr := CurPtr^.prev ;
  422.           LineNumber := LineNumber - 1;
  423.           UnDoCopy := CurPtr^.data ;
  424.           if Row > RowMin then
  425.              begin
  426.              Row := Row-1;
  427.              ReDisplay := false;
  428.              end
  429.           else
  430.              ReDisplay := true;
  431.           end;
  432.      end;
  433.   {----------------------------------------------------------}
  434.   procedure goto_next_line;
  435.      begin
  436.      if CurPtr^.next <> nil then
  437.           begin
  438.           CurPtr := CurPtr^.next ;
  439.           LineNumber := LineNumber + 1;
  440.           UnDoCopy := CurPtr^.data ;
  441.           if Row < RowMax then
  442.              begin
  443.              Row := Row+1;
  444.              ReDisplay := false;
  445.              end
  446.           else
  447.              ReDisplay := true;
  448.           end;
  449.      end;
  450.   { ----------------------------------------------------------------------- }
  451.  
  452.   { Join this and next line, removing extra spaces
  453.     and respecting word boundaries smartly. 17-Jan-95.
  454.   }
  455.   procedure join_lines;
  456.       var i, j, k, lencp : 0..LineSz;
  457.     begin{join_lines}
  458.  
  459.     cleartailspaces( CurPtr );
  460.     lencp := length( CurPtr^.data );
  461.     if col < lencp then  { first go to end of line }
  462.        begin
  463.        col := lencp;
  464.        exit;
  465.        end;
  466.     if lencp = LineSz then  { no space to join }
  467.        begin
  468.        gotoxy(1,RowNum); write('This line is full, going to next line.');
  469.        goto_next_line;
  470.        exit;
  471.        end;
  472.  
  473.     tp2 := CurPtr^.next ;
  474.     if (tp2 <> nil) then
  475.        { so there is a next line to join, and place to join it }
  476.        begin
  477.        col := 1 + lencp;        { keep one space reserved }
  478.        clearleadingspaces( tp2 );
  479.        j := length( tp2^.data );
  480.        k := LineSz - col;      { k+1 cols available on this line }
  481.        i := k;                 { number of chars to move }
  482.  
  483.        if( i < j ) then  { less space, so find a word break. }
  484.           while( (tp2^.data[i+1] <> Space ) and (0<i) ) do
  485.              i:=i-1;
  486.  
  487.        if i < 1 then { Not possible to join, you try on next line. }
  488.           begin
  489.           gotoxy(1,RowNum);
  490.           write('Not enough space to join, going to next line.');
  491.           goto_next_line;
  492.           exit;
  493.           end;
  494.  
  495.        if CurPtr^.data[lencp] <> Space then { need a space at joint }
  496.           CurPtr^.data := CurPtr^.data + Space;
  497.  
  498.        CurPtr^.data := CurPtr^.data + copy( tp2^.data, 1, i );
  499.        delete( tp2^.data, 1, i );
  500.        UnDoCopy := CurPtr^.data ;
  501.  
  502.        clearleadingspaces( tp2 );
  503.        { Now if the next line is empty then delete it }
  504.        if length(tp2^.data) = 0 then
  505.          begin
  506.          if (tp2^.next = nil) then { tp2^ is the last line }
  507.             begin
  508.             CurPtr^.next := nil ;
  509.             BotPtr := CurPtr ;
  510.             end
  511.          else                     { tp2^ is the not last line }
  512.             begin
  513.             tp3 := tp2^.next ;
  514.             tp3^.prev := CurPtr ;
  515.             CurPtr^.next := tp3 ;
  516.             end;
  517.          dispose( tp2 );
  518.          LineCount := LineCount - 1;
  519.          end; { length(tp2^.data) = 0 }
  520.  
  521.        Redisplay := true;
  522.        gotoxy(1,RowNum); write('Joined - this and next lines.');
  523.        { soundclick; }
  524.        end
  525.     else
  526.        begin
  527.        gotoxy(1,RowNum ); write('No more lines to join.');
  528.        delay(100);
  529.        end;
  530.     gotoxy(1,RowNum); clreol;
  531.     end{join_lines};
  532. { ----------------------------------------------------------------------- }
  533.  
  534.   procedure delete_last_char;
  535.     begin
  536.     if col > 1 then
  537.        begin
  538.        col := col -1 ;
  539.        delete( CurPtr^.data, col, 1 );
  540.        end
  541.     end;
  542. { ----------------------------------------------------------------------- }
  543.  
  544.   procedure delete_last_word;   { Tested 26-Feb-94 }
  545.        var i : integer;
  546.      begin{delete_last_word}
  547.      i := col -1;
  548.      if CurPtr^.data[i] = Space then
  549.         while (i > 1) and (CurPtr^.data[i-1] = Space) do
  550.            i := i-1
  551.      else
  552.         while (i > 1) and (CurPtr^.data[i-1] <> Space) do
  553.            i := i-1;
  554.  
  555.      delete( CurPtr^.data, i, col-i );
  556.      col := i;
  557.      end{delete_last_word};
  558. { ----------------------------------------------------------------------- }
  559.  
  560.   procedure delete_next_word;
  561.      { delete word delimited by space, delete atleast one char }
  562.         var i : integer;
  563.      begin
  564.      if length( CurPtr^.data ) = 1 then
  565.         delete_line( CurPtr )
  566.      else
  567.         begin
  568.         i := col;
  569.         if CurPtr^.data[ col ] = Space then
  570.            while (i < length( CurPtr^.data )) and
  571.                  (CurPtr^.data[i+1] = Space) do
  572.                      i := i + 1
  573.         else
  574.            while (i < length( CurPtr^.data )) and
  575.                  (CurPtr^.data[i+1] <> Space) do
  576.                      i := i + 1;
  577.  
  578.         delete( CurPtr^.data, col, i+1-col );
  579.         end;
  580.      end;
  581.   {----------------------------------------------------------}
  582.  
  583.   procedure insert_ten_spaces;
  584.       var i : integer;
  585.      begin
  586.      i := 0 ;
  587.      while (length( CurPtr^.data ) < LineSz) and (i<10) do
  588.         begin
  589.         insert( Space, CurPtr^.data, col );
  590.         col := col + 1 ;
  591.         i := i + 1;
  592.         end ;
  593.      end;
  594.   {----------------------------------------------------------}
  595.   procedure cursor( c : char );   { 07-Jan-94  }
  596.      var { set cursor shape/size }
  597.        cl, ch :  integer;
  598.        result :  registers;
  599.        { record ax,bx,cx,dx,bp,si,di,ds,es,flags : integer; end; }
  600.    begin{cursor}
  601.    result.ah :=  1;
  602.    with result do
  603.     case c of
  604.       'b' {block}  : begin ch := 0; cl := 6; end;
  605.       'd' {double} : begin cl := 1; ch := 6; end;
  606.       'h' {half}   : begin ch := 4; cl := 7; end;
  607.       'l' {line}   : begin ch := 6; cl := 7; end;
  608.       'n' {none}   : begin ch := 8; cl := 1; end;
  609.       't' {top}    : begin ch := 0; cl := 3; end;
  610.       end;
  611.    intr( $10,  result );
  612.    { soundclick; }
  613.    end{cursor};
  614.   { ----------------------------------------------------------------------- }
  615.  
  616.   var Hour,Minute,Second,Second100,
  617.       Year,Month,Day,DayOfWeek       : word;
  618.   procedure DisplayTime;
  619.      begin
  620.      gettime(Hour,Minute,Second,Second100);
  621.      getdate(Year,Month,Day,DayOfWeek);
  622.      gotoxy(1,RowDate);
  623.      write( 'Date: ',Day:2,'-',Month:2,'-',Year:4,', ',
  624.             'Time: ',Hour:2,':',Minute:2,':',Second:2,'.');
  625.      write('                         (C) mosh@cs.albany.edu');
  626.      clreol;
  627.      end;
  628.   { ----------------------------------------------------------------------- }
  629.   procedure DisplayMode;
  630.      begin
  631.      gotoxy(1,RowMode);
  632.      write('File: ',FileName,', Col: ',col:2,', Line: ', LineNumber:3,
  633.            ', LastLine: ', LineCount:3, ', ');
  634.      if InsMode then  write( 'INS')
  635.      else             write( 'OVR');
  636.      clreol;
  637.      end;
  638.   { ----------------------------------------------------------------------- }
  639.   procedure DisplayScreen;
  640.        var i : integer;
  641.      begin
  642.  
  643.      gotoxy(1,row); write( CurPtr^.Data );
  644.      if length( CurPtr^.Data ) < LineSz then clreol ;
  645.  
  646.      { write data to screen }
  647.      if ReDisplay
  648.          {
  649.            or ( LastLineNum <> LineNumber )
  650.            or ( LineCount <> LastCount )
  651.          }
  652.      then
  653.         begin         { line has changed, write prev & next lines }
  654.         {lowvideo;}
  655.         redisplay := false;
  656.  
  657.         tp1 := CurPtr^.prev ;
  658.         i := row-1;
  659.         while (tp1 <> nil) and (i >= RowMin) do
  660.           begin
  661.           gotoxy(1,i); write( tp1^.data );
  662.           if length( tp1^.data ) < LineSz then clreol;
  663.           tp1 := tp1^.prev;
  664.           i := i-1;
  665.           end;
  666.         while i >= RowMin do
  667.           begin
  668.           gotoxy(1,i); write( '@@@' ); clreol; { No Line Present }
  669.           i := i-1;
  670.           end;
  671.  
  672.         tp2 := CurPtr^.next ;
  673.         i := row+1;
  674.         while (tp2 <> nil) and (i <= RowMax) do
  675.           begin
  676.           gotoxy(1,i); write( tp2^.data );
  677.           if length( tp2^.data) < LineSz then clreol;
  678.           tp2 := tp2^.next;
  679.           i := i+1;
  680.           end;
  681.         while i <= RowMax do
  682.           begin
  683.           gotoxy(1,i); write( '@@@' ); clreol; { No Line Present }
  684.           i := i+1;
  685.           end;
  686.  
  687.         LastLineNum := LineNumber ;
  688.         LastCount := LineCount ;
  689.         {normvideo;}
  690.         end;
  691.  
  692.      gotoxy(col,row);  { place cursor }
  693.      end;
  694. { ----------------------------------------------------------------------- }
  695.  
  696.   procedure DisplayBoundary;
  697.     begin { -- draw a double-lined box around the edit window -- }
  698.     gotoxy(1,RowTop) ;
  699.     write(#201);
  700.     for i := 2 to 78 do
  701.       write(#205);
  702.     write(#187);
  703.     gotoxy(1,RowBot) ;
  704.     write(#200);
  705.     for i := 2 to 78 do
  706.       write(#205);
  707.     write(#188);
  708.   end;
  709.  
  710.   { ----------------------------------------------------------------------- }
  711.   { F1/F2 change (+/-) foreground color,
  712.     F3/F4 change (+/-) background color
  713.   }
  714.   Const Fcolor : integer  = White;  { Initialized Variables }
  715.         Bcolor : integer  = Black;
  716.   procedure ScreenColors( key : char );
  717.      begin
  718.      case key of
  719.          {f1} #59 : Fcolor := (Fcolor +1) ;
  720.          {f2} #60 : Fcolor := (Fcolor -1) ;
  721.          {f3} #61 : Bcolor := (Bcolor +1) ;
  722.          {f4} #62 : Bcolor := (Bcolor -1) ;
  723.          end;
  724.      if Fcolor > 16 then Fcolor := Fcolor -16;
  725.      if Fcolor <  0 then Fcolor := Fcolor +16;
  726.      if Bcolor >  8 then Bcolor := Bcolor -16;
  727.      if Bcolor <  0 then Bcolor := Bcolor +16;
  728.      textcolor( Fcolor );
  729.      textbackground( Bcolor );
  730.      for i := 1 to 25 do
  731.         begin
  732.         gotoxy(1,i); clreol;
  733.         end;
  734.      DisplayBoundary;
  735.      ReDisplay := true;
  736.      end;
  737.  
  738.   procedure ProcessNormal( ikey : char );
  739.      begin
  740.      case ord( ikey ) of
  741.        { backspace }    8 :     delete_last_char;
  742.        { ^backspace } 127 :     delete_last_word;
  743.        { ^T }          20 :     delete_next_word;
  744.        { ^Y }          25 :     delete_line( CurPtr );
  745.        { tab }          9 :     insert_ten_spaces;
  746.        { ^J }          10 :     join_lines;
  747.        { return }      13 :
  748.          if InsMode then
  749.             breakline( false )  { breakline exactly at current column }
  750.          else { not InsMode, just go to next line }
  751.             begin
  752.             goto_next_line;
  753.             col := 1;
  754.             end;
  755.        { printable key }  32 .. 125  :
  756.           begin
  757.           if not InsMode then  { overwite or insert }
  758.              begin {Overwrite}
  759.              CurPtr^.data[ col ] := ikey;
  760.              col := col + 1;
  761.              if (length( CurPtr^.data ) < col ) and (col < LineSz) then
  762.                 CurPtr^.data := CurPtr^.data + Space;
  763.              end
  764.           else
  765.              begin {Insert}
  766.              if length( CurPtr^.data ) < LineSz then
  767.                 begin
  768.                 insert( ikey, CurPtr^.data, col );
  769.                 col := col + 1;
  770.                 end
  771.              else if (col >= LineSz) then
  772.                 begin
  773.                 breakline( true ); { break at a word-boundary }
  774.                 insert( ikey, CurPtr^.data, col );
  775.                 col := col + 1;
  776.                 end
  777.              else
  778.                 RingBell;
  779.              end; {Insert}
  780.           end; {printable key}
  781.        end; { case }
  782.      end;
  783. { ----------------------------------------------------------------------- }
  784.   procedure ProcessMetaKey( cmdkey : char );
  785.      begin
  786.      case cmdkey of
  787.       'u' : {undo}
  788.             begin
  789.             TmpCopy := CurPtr^.data ;
  790.             CurPtr^.data := UnDoCopy ;
  791.             UnDoCopy := TmpCopy ;
  792.             end;
  793.       'f' : {new file}
  794.             begin
  795.             write(',    Current File name : ');
  796.             readln( FileName );
  797.             end;
  798.       '/' : searchstring;
  799.       'y'  : { yank line }
  800.              YankCopy := CurPtr^.data ;
  801.       'Y' :  { yank to end of line }
  802.              YankCopy := copy( CurPtr^.data, col, 255 ) ;
  803.       'p' :  { put below current line, and cursor on it }
  804.              begin
  805.              new( tp2 );
  806.              tp1 := CurPtr ;
  807.              tp2^.next := tp1^.next ;
  808.              tp2^.prev := tp1 ;
  809.              tp1^.next := tp2 ;
  810.              tp3 := tp2^.next ;
  811.              tp3^.prev := tp2 ;
  812.              CurPtr := tp2 ;
  813.              tp2^.data := YankCopy ;
  814.              col := 1 ;
  815.              LineCount := LineCount + 1 ;
  816.              LineNumber := LineNumber + 1 ;
  817.              ReDisplay := true;
  818.              end;
  819.       'j' : join_lines;
  820.       'P' : { Put as many chars as possible at eol }
  821.             insert( CurPtr^.data, YankCopy, col );
  822.       'd' : delete_line( CurPtr );
  823.       'm' : { mark line }
  824.             begin
  825.             LineMark := CurPtr;
  826.             write('   Mark set.');
  827.             delay(100);
  828.             end;
  829.       'D' : { delete lines till mark }
  830.             if LineMark <> Nil then
  831.                 begin
  832.                 while (CurPtr <> LineMark) and (CurPtr <> BotPtr) do
  833.                     delete_line( CurPtr );
  834.                 LineMark := Nil; { unset mark after deletion }
  835.                 ReDisplay := true;
  836.                 end
  837.             else
  838.                 begin
  839.                 write('    Mark not set ?');
  840.                 { RingBell; }
  841.                 delay(100);
  842.                 end;
  843.       'w' : if getname( FileNameTmp ) then
  844.                 writefile( FileNameTmp );
  845.       'r' : if getname( FileNameTmp ) then
  846.                 readfile( FileNameTmp );
  847.       end; { case cmdkey }
  848.      end;
  849. { ----------------------------------------------------------------------- }
  850.   procedure ProcessExtKey( extkey : char );
  851.      begin
  852.      case extkey of
  853.          {home} #71  : col := 1;
  854.          {end}  #79  : col := length( CurPtr^.data );
  855.          {^home}#119 : begin
  856.                        delete( CurPtr^.data, 1, col-1 );
  857.                        col := 1;
  858.                        end;
  859.          {^end}#117  : begin
  860.                        delete( CurPtr^.data, col+1, 255 );
  861.                        end;
  862.          {l-ar}#75   : col := col -1 ;
  863.          {r-ar}#77   :
  864.                 begin
  865.                 col := col + 1 ;
  866.                 if ( col > length( CurPtr^.data ) ) and
  867.                    ( length( CurPtr^.data ) < LineSz )
  868.                 then
  869.                   insert( Space, CurPtr^.data,length( CurPtr^.data)+1);
  870.                 end;
  871.          {c-l-ar}#115 :
  872.                   begin
  873.                   if col = 1 then
  874.                     begin
  875.                     goto_previous_line;
  876.                     col := length( CurPtr^.data );
  877.                     end
  878.                   else
  879.                     begin
  880.                     repeat
  881.                       col := col-1;
  882.                       until (col <= 1) or
  883.                             ((CurPtr^.data[col-1] = Space) and
  884.                              (CurPtr^.data[col] <> Space) );
  885.                     end;
  886.                   end;
  887.          {c-r-ar}#116 :
  888.                   begin
  889.                   if (col = length( CurPtr^.data)) then
  890.                     begin
  891.                     goto_next_line;
  892.                     col := 1;
  893.                     end
  894.                   else
  895.                     begin
  896.                     repeat
  897.                       col := col+1;
  898.                       until (col >= length( CurPtr^.data)) or
  899.                             ((CurPtr^.data[col-1] = Space) and
  900.                              (CurPtr^.data[col] <> Space) );
  901.                     end;
  902.                   end;
  903.          {u-ar}#72 : goto_previous_line;
  904.          {d-ar}#80 : goto_next_line;
  905.          {pg-up}#73 :
  906.                 begin
  907.                 i := 1;
  908.                 while (i <= 15) and (CurPtr^.prev <> nil) do
  909.                      begin
  910.                      CurPtr := CurPtr^.prev ;
  911.                      LineNumber := LineNumber - 1;
  912.                      i := i+1;
  913.                      ReDisplay := true;
  914.                      end;
  915.                 if i > 1 then
  916.                    UnDoCopy := CurPtr^.data ;
  917.                 end;
  918.          {pg-dn}#81 :
  919.                 begin
  920.                 i := 1;
  921.                 while (i <= 15) and (CurPtr^.next <> nil) do
  922.                      begin
  923.                      CurPtr := CurPtr^.next ;
  924.                      LineNumber := LineNumber + 1;
  925.                      i := i+1;
  926.                      ReDisplay := true;
  927.                      end;
  928.                 if i > 1 then
  929.                      UnDoCopy := CurPtr^.data ;
  930.                 end;
  931.          {c-pg-up}#132 :
  932.                 if CurPtr <> TopPtr then
  933.                    begin
  934.                    CurPtr := TopPtr ;
  935.                    LineNumber := 1 ;
  936.                    UnDoCopy := CurPtr^.data ;
  937.                    ReDisplay := true;
  938.                    end;
  939.          {c-pg-dn}#118 :
  940.                 if CurPtr <> BotPtr then
  941.                    begin
  942.                    CurPtr := BotPtr ;
  943.                    LineNumber := LineCount ;
  944.                    UnDoCopy := CurPtr^.data ;
  945.                    ReDisplay := true;
  946.                    end;
  947.          {ins}#82 : begin
  948.                     InsMode := not( InsMode );
  949.                     if InsMode then cursor( 'b' )
  950.                     else            cursor( 'l' );
  951.                     end;
  952.          {del}#83 :
  953.                 if ( length( CurPtr^.data ) > 0 ) then
  954.                    delete( CurPtr^.data, col, 1 );
  955.          {f1-f4} #59,#60,#61,#62 :
  956.                 screencolors( ExtKey );
  957.         end; { case }
  958.      end;
  959. { ----------------------------------------------------------------------- }
  960.  
  961. begin{main}
  962. clrscr;
  963. randomize ;
  964.  
  965. CheckSnow := true;      { For CGA Color  }
  966. DirectVideo := true;    { No BIOS for IO }
  967.  
  968. LineCount := 1;         { Always start with a line }
  969. new( CurPtr );
  970. CurPtr^.prev := nil ;
  971. CurPtr^.next := nil ;
  972. CurPtr^.data := '';
  973. TopPtr := CurPtr ;
  974. BotPtr := CurPtr ;
  975.  
  976. { readfile always inserts lines or adds lines so delete the first line }
  977. if TopPtr <> BotPtr then
  978.     begin
  979.     CurPtr := CurPtr^.next;
  980.     dispose( CurPtr^.prev );
  981.     CurPtr^.prev := nil ;
  982.     TopPtr := CurPtr;
  983.     LineCount := LineCount - 1;
  984.     end;
  985. LineNumber := 1;
  986. LastLineNum := 0;
  987.  
  988. if ( paramcount >= 1 ) then
  989.       begin
  990.       FileName := paramstr(1);
  991.       readfile( FileName );
  992.       end
  993. else  FileName := 'editor.tmp';
  994.  
  995. { Begin Edit }
  996. col := 1;
  997. row := RowMin;
  998. InsMode := true;
  999. ReDisplay := true;
  1000. cursor('b');       { Block cursor for insert-mode }
  1001. UnDoCopy := CurPtr^.data ;
  1002. YankCopy := '';
  1003. LastCount := LineCount ;
  1004. cmdkey := Space;
  1005. LineMark := nil;
  1006. Laststr := Space;
  1007. clrscr ;
  1008. DisplayBoundary;
  1009. repeat  { main loop }
  1010.  
  1011.      if length( CurPtr^.data ) =  0 then
  1012.         CurPtr^.data := Space;
  1013.  
  1014.      { make sure that col in [1,..,datalength] }
  1015.      if col < 1 then
  1016.         col := 1
  1017.      else if col > length( CurPtr^.data ) then
  1018.         col := length( CurPtr^.data );
  1019.  
  1020.      cursor('n');    { no cursor while screen updates }
  1021.      DisplayTime;
  1022.      DisplayMode;
  1023.      DisplayScreen;
  1024.      if InsMode then cursor( 'b' )
  1025.      else            cursor( 'l' );
  1026.  
  1027.  
  1028.      ikey := readkey; { next key }
  1029.      if ( ikey <> ExtKeyCode ) and ( ikey <> ESC ) then
  1030.         { nonextended/nonmeta key }
  1031.         ProcessNormal( ikey );
  1032.  
  1033.      if ( ikey = ExtKeyCode ) then { extended key }
  1034.            begin
  1035.            extkey := readkey;
  1036.            ProcessExtKey( extkey );
  1037.            end;
  1038.  
  1039.      if ( ikey = ESC ) then
  1040.            begin { ESC, read cmdkey }
  1041.            gotoxy(3,RowMeta); write('[0-9+-uf/kyYpPjdDwWrRxXq]: ESC-');
  1042.            cmdkey := readkey; write( cmdkey );
  1043.            ProcessMetaKey( cmdkey );
  1044.            gotoxy( 1,RowMeta ); clreol ; { clear Metakey line }
  1045.            gotoxy( 1,RowNum ); clreol ; { clear line }
  1046.            delay(100);  { so that user can see the input }
  1047.            end; { ESC-processing }
  1048.  
  1049.   until cmdkey in ['x','X','q','Q'] ;
  1050.  
  1051. clrscr;
  1052. if cmdkey = 'x' then writefile( FileName );
  1053. clrscr;
  1054. end{main}.
  1055.  
  1056.  
  1057.